/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package com.lineage.server.packetclient;

import static com.lineage.server.model.identity.L1ItemUseTypeId.AMULET;
import static com.lineage.server.model.identity.L1ItemUseTypeId.ARMOR;
import static com.lineage.server.model.identity.L1ItemUseTypeId.BELT;
import static com.lineage.server.model.identity.L1ItemUseTypeId.BLANK;
import static com.lineage.server.model.identity.L1ItemUseTypeId.BOOTS;
import static com.lineage.server.model.identity.L1ItemUseTypeId.BTELE;
import static com.lineage.server.model.identity.L1ItemUseTypeId.CCARD;
import static com.lineage.server.model.identity.L1ItemUseTypeId.CHOICE;
import static com.lineage.server.model.identity.L1ItemUseTypeId.CLOAK;
import static com.lineage.server.model.identity.L1ItemUseTypeId.COOKING_BOOKS;
import static com.lineage.server.model.identity.L1ItemUseTypeId.DAI;
import static com.lineage.server.model.identity.L1ItemUseTypeId.DEL;
import static com.lineage.server.model.identity.L1ItemUseTypeId.EARRING;
import static com.lineage.server.model.identity.L1ItemUseTypeId.FISHING_ROD;
import static com.lineage.server.model.identity.L1ItemUseTypeId.GLOVE;
import static com.lineage.server.model.identity.L1ItemUseTypeId.HELN;
import static com.lineage.server.model.identity.L1ItemUseTypeId.IDENTIFY;
import static com.lineage.server.model.identity.L1ItemUseTypeId.LETTER;
import static com.lineage.server.model.identity.L1ItemUseTypeId.NONE;
import static com.lineage.server.model.identity.L1ItemUseTypeId.NORMAL;
import static com.lineage.server.model.identity.L1ItemUseTypeId.NTELE;
import static com.lineage.server.model.identity.L1ItemUseTypeId.RES;
import static com.lineage.server.model.identity.L1ItemUseTypeId.RING;
import static com.lineage.server.model.identity.L1ItemUseTypeId.SHIELD;
import static com.lineage.server.model.identity.L1ItemUseTypeId.SOSC;
import static com.lineage.server.model.identity.L1ItemUseTypeId.SPELL_BUFF;
import static com.lineage.server.model.identity.L1ItemUseTypeId.SPELL_LONG;
import static com.lineage.server.model.identity.L1ItemUseTypeId.SPELL_SHORT;
import static com.lineage.server.model.identity.L1ItemUseTypeId.T;
import static com.lineage.server.model.identity.L1ItemUseTypeId.TATTOO_L;
import static com.lineage.server.model.identity.L1ItemUseTypeId.TATTOO_M;
import static com.lineage.server.model.identity.L1ItemUseTypeId.TATTOO_R;
import static com.lineage.server.model.identity.L1ItemUseTypeId.TREASURE_BOX;
import static com.lineage.server.model.identity.L1ItemUseTypeId.VCARD;
import static com.lineage.server.model.identity.L1ItemUseTypeId.WCARD;
import static com.lineage.server.model.identity.L1ItemUseTypeId.WEAPON;
import static com.lineage.server.model.identity.L1ItemUseTypeId.XML;
import static com.lineage.server.model.identity.L1ItemUseTypeId.ZEL;
import static com.lineage.server.model.skill.L1SkillId.SOLID_CARRIAGE;

import java.sql.Timestamp;
import java.util.Calendar;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.lineage.server.ClientThread;
import com.lineage.server.model.L1ItemDelay;
import com.lineage.server.model.L1PcInventory;
import com.lineage.server.model.L1PolyMorph;
import com.lineage.server.model.Instance.L1ItemInstance;
import com.lineage.server.model.Instance.L1PcInstance;
import com.lineage.server.model.item.ItemClass;
import com.lineage.server.model.item.L1TreasureBox;
import com.lineage.server.packetserver.S_OwnCharAttrDef;
import com.lineage.server.packetserver.S_OwnCharStatus;
import com.lineage.server.packetserver.S_PacketBox;
import com.lineage.server.packetserver.S_SPMR;
import com.lineage.server.packetserver.S_ServerMessage;
import com.lineage.server.packetserver.ServerBasePacket;
import com.lineage.server.templates.L1EtcItem;
import com.lineage.server.utils.CheckUtil;

/**
 * Class <code>ClientsUseItem</code> 处理由客户端传来使用道具的封包.
 * 
 * @author jrwz
 * @version 2012-6-18下午13:45:54
 * @see com.lineage.server.packetclient
 * @since JDK1.7
 */
public final class ClientsUseItem extends ClientBasePacket {

    /** 返回一个字符串, 表示客户端的数据包类型. */
    private static final String C_USE_ITEM = "[C] ClientsUseItem";
    /** 提示信息. */
    private static final Log LOG = LogFactory.getLog(ClientsUseItem.class);

    /**
     * 处理由客户端传来使用道具的封包.
     * 
     * @param abyte0
     *            资料
     * @param client
     *            客户端对象
     */
    public ClientsUseItem(final byte[] abyte0, final ClientThread client) {
        super(abyte0); // 载入资料
        final L1PcInstance pc = client.getActiveChar();
        if (pc == null) {
            return;
        }
        // 检测(无法使用道具的情况)
        if (!this.check(pc)) {
            return;
        }
        final int itemObjid = this.readD();
        final L1ItemInstance useItem = pc.getInventory().getItem(itemObjid);
        if (useItem == null) {
            return;
        }
        if (useItem.getCount() <= 0) {
            return;
        }
        if (pc.getCurrentHp() > 0) {
            // 检测(当前等级能否使用道具)
            if (!this.checkUseLevel(pc, useItem)) {
                return;
            }
            int delayId = 0; // 延迟ID
            if (useItem.getItem().getType2() == 0) { // 使用类型为道具 (etcitem)
                delayId = ((L1EtcItem) useItem.getItem()).getDelayId();
            }
            if (delayId != 0) {
                if (pc.hasItemDelay(delayId)) {
                    return;
                }
            }
            pc.setDelayEffect(false);
            int delayEffect = 0; // 延迟时间(效果)
            if (useItem.getItem().getType2() == 0) { // 使用类型为道具 (etcitem)
                delayEffect = ((L1EtcItem) useItem.getItem()).getDelayEffect(); // 延迟时间
            }
            if (delayEffect > 0) {
                pc.setDelayEffect(true);
                final Timestamp lastUsed = useItem.getLastUsed();
                if (lastUsed != null) {
                    final Calendar cal = Calendar.getInstance();
                    long useTime = (cal.getTimeInMillis() - lastUsed.getTime()) / 1000;
                    if (useTime <= delayEffect) {
                        // 转换为须等待时间
                        useTime = (delayEffect - useTime) / 60;
                        final ServerBasePacket msg = new S_ServerMessage(1139,
                                String.valueOf(useTime));
                        pc.sendPackets(msg); // %0 分钟之内无法使用。
                        return;
                    }
                }
            }

            this.itemUseType(pc, useItem); // 检测(道具的使用类型)

            // 物件使用延迟设置
            if (pc.isDelayEffect()) {
                final Timestamp ts = new Timestamp(System.currentTimeMillis());
                useItem.setLastUsed(ts); // 设置使用时间
                pc.getInventory().updateItem(useItem,
                        L1PcInventory.COL_DELAY_EFFECT);
                pc.getInventory().saveItem(useItem,
                        L1PcInventory.COL_DELAY_EFFECT);
            }
            try {
                L1ItemDelay.onItemUse(client, useItem); // 项目开始延迟
            } catch (final Exception e) {
                LOG.error("道具使用延迟异常:" + useItem.getItemId(), e);
            }
        }
    }

    /**
     * 检测(无法使用道具的情况).
     * 
     * @param pc
     *            对象
     * @return true:可用、false:不可用
     */
    private boolean check(final L1PcInstance pc) {
        boolean flag = true;
        if (pc != null) {
            if (pc.isDead()) {
                flag = false; // 死亡状态
            }
            if (pc.isGhost() || pc.isTeleport() || pc.isPrivateShop()) {
                flag = false; // 幽灵状态、传送状态、开个人商店中
            }
            if (!pc.getMap().isUsableItem()) {
                final S_ServerMessage msg = new S_ServerMessage(563);
                pc.sendPackets(msg); // \f1你无法在这个地方使用。
                flag = false; // 不能使用道具的地图
            }
            final boolean isUse = CheckUtil.isCanNotDoAnything(pc);
            if (isUse) {
                flag = false; // 什么都不能做的状态
            }
        }
        return flag;
    }

    /**
     * 检测(当前等级能否使用道具).
     * 
     * @param pc
     *            -对象
     * @param useItem
     *            -道具
     * @return true:可用 、false:不可用
     */
    private boolean checkUseLevel(final L1PcInstance pc,
            final L1ItemInstance useItem) {
        boolean is = true;
        final int min = useItem.getItem().getMinLevel(); // 取得可以使用道具的最低等级
        final int max = useItem.getItem().getMaxLevel(); // 取得可以使用道具的最高等级
        if ((min != 0) && (min > pc.getLevel())) {
            final S_ServerMessage msg = new S_ServerMessage(318,
                    String.valueOf(min));
            pc.sendPackets(msg); // 等级%0以上才可使用此道具。
            is = false;
        } else if ((max != 0) && (max < pc.getLevel())) {
            pc.sendPackets(new S_PacketBox(S_PacketBox.MSG_LEVEL_OVER, max));
            is = false; // 等级%d以下才能使用此道具。
        }
        return is;
    }

    @Override
    public String getType() {
        return C_USE_ITEM;
    }

    /**
     * 检查武器与防具该职业能否使用.
     * 
     * @param pc
     *            角色
     * @param useItem
     *            使用的装备
     * @return 如果该职业可用、true
     */
    private boolean isEquipped(final L1PcInstance pc,
            final L1ItemInstance useItem) {
        boolean isEquipped = false;

        if (pc.isCrown()) { // 王族
            if (useItem.getItem().isUseRoyal()) {
                isEquipped = true;
            }
        } else if (pc.isKnight()) { // 骑士
            if (useItem.getItem().isUseKnight()) {
                isEquipped = true;
            }
        } else if (pc.isElf()) { // 精灵
            if (useItem.getItem().isUseElf()) {
                isEquipped = true;
            }
        } else if (pc.isWizard()) { // 魔法师
            if (useItem.getItem().isUseMage()) {
                isEquipped = true;
            }
        } else if (pc.isDarkelf()) { // 黑暗精灵
            if (useItem.getItem().isUseDarkelf()) {
                isEquipped = true;
            }
        } else if (pc.isDragonKnight()) { // 龙骑士
            if (useItem.getItem().isUseDragonknight()) {
                isEquipped = true;
            }
        } else if (pc.isIllusionist()) { // 幻术师
            if (useItem.getItem().isUseIllusionist()) {
                isEquipped = true;
            }
        }

        if (!isEquipped) {
            pc.sendPackets(new S_ServerMessage(264)); // \f1你的职业无法使用此装备。
        }
        return isEquipped;
    }

    /**
     * 检测(道具的使用类型).
     * 
     * @param pc
     *            -对象
     * @param useItem
     *            -道具
     */
    private void itemUseType(final L1PcInstance pc, final L1ItemInstance useItem) {
        final int useType = useItem.getItem().getUseType();
        boolean isExecute = false;
        boolean isExecuteXML = false;
        int readC1 = 0;
        int readC2 = 0;
        int targetId = 0;
        int targetX = 0;
        int targetY = 0;
        switch (useType) {
            case XML: // XML类型
                isExecuteXML = true;
                break;
            case NORMAL: // 一般类道具
                isExecute = true;
                break;
            case COOKING_BOOKS: // 料理书
                readC1 = this.readC();
                readC2 = this.readC();
                isExecute = true;
                break;
            case SPELL_LONG: // 魔杖类型 (须选取目标/坐标)地面 / 选择对象(远距离)
            case SPELL_SHORT: // 选取目标 地面 (近距离)
                targetId = this.readD(); // 选取目标的OBJID
                targetX = this.readH(); // X坐标
                targetY = this.readH(); // Y坐标
                isExecute = true;
                break;
            case NTELE: // 瞬间移动卷轴
            case BTELE: // 瞬间移动卷轴(祝福)
                this.readH(); // 所在地图编号 /必须在上方
                targetId = this.readD(); // 选取目标的OBJID
                isExecute = true;
                break;
            case IDENTIFY: // 鉴定卷轴
            case RES: // 复活卷轴
            case CHOICE: // 请选择一个物品 (道具栏位) 灯油/磨刀石/胶水/龙之魔眼等
            case DAI: // 对武器施法的卷轴
            case ZEL: // 对盔甲施法的卷轴
            case SPELL_BUFF: // 选取目标 (对NPC需要Ctrl 远距离 无XY坐标传回) / 魔法卷轴 (已写)
            case DEL: // 饰品强化卷轴
                targetId = this.readD(); // 选取物件的OBJID
                isExecute = true;
                break;
            case LETTER: // 信纸
            case CCARD: // 圣诞卡片
            case VCARD: // 情人节卡片
            case WCARD: // 白色情人节卡片
                targetX = this.readH();
                final String s = this.readS();
                pc.setText(s);
                pc.setTextByte(this.readByte());
                isExecute = true;
                break;
            case SOSC: // 变形卷轴
                final String cmd = this.readS();
                pc.setText(cmd); // 选取的变身命令
                isExecute = true;
                break;
            case BLANK: // 空的魔法卷轴
                readC1 = this.readC();
                isExecute = true;
                break;
            case FISHING_ROD: // 钓鱼杆 (魔法钓竿)
                targetX = this.readH(); // X坐标
                targetY = this.readH(); // Y坐标
                isExecute = true;
                break;
            case NONE: // 无法使用 (材料等)
                final S_ServerMessage msg2 = new S_ServerMessage(74,
                        useItem.getLogName());
                pc.sendPackets(msg2); // \f1%0%o无法使用。
                break;
            case WEAPON: // 武器
                try {
                    if (this.isEquipped(pc, useItem)) {
                        this.setEquippedWeapon(pc, useItem);
                    }
                } catch (final Exception e) {
                    LOG.error("道具使用异常(武器):" + useItem.getItemId(), e);
                }
                break;
            case ARMOR: // 盔甲
            case T: // T恤
            case CLOAK: // 斗篷
            case GLOVE: // 手套
            case BOOTS: // 长靴
            case HELN: // 头盔
            case RING: // 戒指
            case AMULET: // 项链
            case SHIELD: // 盾牌
            case BELT: // 腰带
            case EARRING: // 耳环
            case TATTOO_R: // 辅助装备 (右)
            case TATTOO_L: // 辅助装备 (左)
            case TATTOO_M: // 辅助装备 (中)
                try {
                    if (this.isEquipped(pc, useItem)) {
                        this.setEquippedArmor(pc, useItem);
                    }
                } catch (final Exception e) {
                    LOG.error("道具使用异常(装备):" + useItem.getItemId(), e);
                }
                break;
            case TREASURE_BOX: // treasure_box (宝箱类)
                final int itemId = useItem.getItem().getItemId();
                final L1TreasureBox box = L1TreasureBox.get(itemId);
                if (box != null) {
                    if (box.open(pc)) {
                        final L1EtcItem temp2 = (L1EtcItem) useItem.getItem();
                        if (temp2.getDelayEffect() > 0) {
                            // 有限制再次使用的时间且可堆叠的道具
                            if (useItem.isStackable()) {
                                if (useItem.getCount() > 1) {
                                    pc.setDelayEffect(true);
                                }
                                pc.getInventory()
                                        .removeItem(useItem.getId(), 1);
                            } else {
                                pc.setDelayEffect(true);
                            }
                        } else {
                            pc.getInventory().removeItem(useItem.getId(), 1);
                        }
                    }
                }
                break;
            default:
                break;
        }
        if (isExecuteXML) {
            ItemClass.getInstance().itemXML(pc, useItem, readC1, readC2,
                    targetId, targetX, targetY);
        }
        if (isExecute) {
            ItemClass.getInstance().item(pc, useItem, readC1, readC2, targetId,
                    targetX, targetY);
        }
    }

    /**
     * 设置防具的装备.
     * 
     * @param pc
     *            角色
     * @param armor
     *            装备的防具
     */
    private void setEquippedArmor(final L1PcInstance pc,
            final L1ItemInstance armor) {

        // 取回防具类型
        final int type = armor.getItem().getType();

        // 取回角色背包道具
        final L1PcInventory pcInventory = pc.getInventory();

        // 装备栏是否有空位
        boolean equipeSpace;

        // 戒指可戴2个,其他都只能戴1个
        if (type == 9) {
            equipeSpace = pcInventory.getTypeEquipped(2, 9) <= 1;
        } else {
            equipeSpace = pcInventory.getTypeEquipped(2, type) <= 0;
        }

        // 防具穿戴
        if (equipeSpace && !armor.isEquipped()) { // 要安装的装备栏尚未安装物品

            // 取回变身编号
            final int polyid = pc.getTempCharGfx();

            // 在此变身状态不能装备的装备
            if (!L1PolyMorph.isEquipableArmor(polyid, type)) {
                return;
            }

            // 已经装备其他东西。
            if (((type == 13) && (pcInventory.getTypeEquipped(2, 7) >= 1))
                    || ((type == 7) && (pcInventory.getTypeEquipped(2, 13) >= 1))) {
                pc.sendPackets(new S_ServerMessage(124)); // 已经装备其他东西。
                return;
            }

            // 使用双手武器时无法装备盾牌
            if ((type == 7) && (pc.getWeapon() != null)) {
                if (pc.getWeapon().getItem().isTwohandedWeapon()) { // 双手武器
                    pc.sendPackets(new S_ServerMessage(129)); // \f1当你使用双手武器时，无法装备盾牌。
                    return;
                }
            }

            // 穿着斗篷时不可穿内衣
            if ((type == 3) && (pcInventory.getTypeEquipped(2, 4) >= 1)) {
                pc.sendPackets(new S_ServerMessage(126, "$224", "$225")); // \f1穿着%1
                                                                          // 无法装备
                                                                          // %0%o
                                                                          // 。
                return;
            }

            // 穿着盔甲时不可穿内衣
            else if ((type == 3) && (pcInventory.getTypeEquipped(2, 2) >= 1)) {
                pc.sendPackets(new S_ServerMessage(126, "$224", "$226")); // \f1穿着%1
                                                                          // 无法装备
                                                                          // %0%o
                                                                          // 。
                return;
            }

            // 穿着斗篷时不可穿盔甲
            else if ((type == 2) && (pcInventory.getTypeEquipped(2, 4) >= 1)) {
                pc.sendPackets(new S_ServerMessage(126, "$226", "$225")); // \f1穿着%1
                                                                          // 无法装备
                                                                          // %0%o
                                                                          // 。
                return;
            }
            pcInventory.setEquipped(armor, true);
        }

        // 防具脱除
        else if (armor.isEquipped()) { // 所选防具穿戴在身上
            if (armor.getItem().getBless() == 2) { // 被诅咒的装备
                pc.sendPackets(new S_ServerMessage(150)); // \f1你无法这样做。这个物品已经被诅咒了。
                return;

            }
            // 穿着盔甲时不能脱下内衣
            if ((type == 3) && (pcInventory.getTypeEquipped(2, 2) >= 1)) {
                pc.sendPackets(new S_ServerMessage(127)); // \f1你不能够脱掉那个。
                return;

            }

            // 穿着斗篷时不能脱下内衣
            else if (((type == 2) || (type == 3))
                    && (pcInventory.getTypeEquipped(2, 4) >= 1)) {
                pc.sendPackets(new S_ServerMessage(127)); // \f1你不能够脱掉那个。
                return;
            }

            // 解除坚固防御
            if (type == 7) {
                if (pc.hasSkillEffect(SOLID_CARRIAGE)) {
                    pc.removeSkillEffect(SOLID_CARRIAGE);
                }
            }
            pcInventory.setEquipped(armor, false);
        } else {
            if (armor.getItem().getUseType() == 23) {
                pc.sendPackets(new S_ServerMessage(144)); // \f1你已经戴着二个戒指。
                return;
            }
            pc.sendPackets(new S_ServerMessage(124)); // \f1已经装备其他东西。
            return;
        }
        pc.setCurrentHp(pc.getCurrentHp()); // 更新角色HP
        pc.setCurrentMp(pc.getCurrentMp()); // 更新角色MP
        pc.sendPackets(new S_OwnCharAttrDef(pc)); // 更新角色物理防御与四属性防御
        pc.sendPackets(new S_OwnCharStatus(pc)); // 更新角色属性与能力值
        pc.sendPackets(new S_SPMR(pc)); // 更新角色魔法攻击与魔法防御
    }

    /**
     * 设置武器的装备.
     * 
     * @param pc
     *            角色
     * @param weapon
     *            装备的武器
     */
    private void setEquippedWeapon(final L1PcInstance pc,
            final L1ItemInstance weapon) {

        // 取回角色背包道具
        final L1PcInventory pcInventory = pc.getInventory();

        // 没有使用武器或使用武器与所选武器不同
        if ((pc.getWeapon() == null) || !pc.getWeapon().equals(weapon)) {

            // 取回武器类型
            final int weapon_type = weapon.getItem().getType();

            // 取回变身编号
            final int polyid = pc.getTempCharGfx();

            // 此变身状态不能使用的武器
            if (!L1PolyMorph.isEquipableWeapon(polyid, weapon_type)) {
                return;
            }

            // 装备盾牌时不可再使用双手武器
            if (weapon.getItem().isTwohandedWeapon()
                    && (pcInventory.getTypeEquipped(2, 7) >= 1)) {
                pc.sendPackets(new S_ServerMessage(128));
                return;
            }
        }

        // 已有装备的状态
        if (pc.getWeapon() != null) {
            // 被诅咒的装备
            if (pc.getWeapon().getItem().getBless() == 2) {
                pc.sendPackets(new S_ServerMessage(150)); // \f1你无法这样做。这个物品已经被诅咒了。
                return;
            }

            // 解除装备
            if (pc.getWeapon().equals(weapon)) {
                pcInventory.setEquipped(pc.getWeapon(), false, false, false);
                return;
            }
            pcInventory.setEquipped(pc.getWeapon(), false, false, true);
        }

        // 被诅咒的装备
        if (weapon.getItem().getBless() == 2) {
            pc.sendPackets(new S_ServerMessage(149, weapon.getLogName())); // \f1%0%s
                                                                           // 主动固定在你的手上！
        }
        pcInventory.setEquipped(weapon, true, false, false);
    }
}
